/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.oai.metadataRepository.transformation;

import cz.mzk.oai.metadataRepository.metadata.MetadataFormat;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.Document;

/**
 *
 * @author Martin Řehánek (rehan at mzk.cz)
 */
public class SecondaryRecordsBuilder {

    private static final BuilderCache builderCache = new BuilderCache();
    private final Map<MetadataFormat, Map<MetadataFormat, SingleSourceTransformer>> map = new HashMap<MetadataFormat, Map<MetadataFormat, SingleSourceTransformer>>();

    public SecondaryRecordsBuilder(Set<XsltConfig> configs) {
        for (XsltConfig config : configs) {
            //System.out.println("config " + config.getXsltFile().getAbsolutePath());
            updateBuilderIfNoConflict(config);
        }
    }

    private void updateBuilderIfNoConflict(XsltConfig config) {
        try {
            updateBuilder(config);
        } catch (Exception ex) {
            Logger.getLogger(SecondaryRecordsBuilder.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void updateBuilder(XsltConfig config) throws Exception {
        MetadataFormat fromFormat = config.getFromFormat();
        MetadataFormat toFormat = config.getToFormat();
        Map<MetadataFormat, SingleSourceTransformer> resultFormatBuilderMap = getResultFormatBuilder(fromFormat);
        checkResultFormatNotPresent(resultFormatBuilderMap, fromFormat, toFormat);
        SingleSourceTransformer builder = createBuilder(fromFormat, toFormat, config.getXsltFile());
        resultFormatBuilderMap.put(toFormat, builder);
    }

    private Map<MetadataFormat, SingleSourceTransformer> getResultFormatBuilder(MetadataFormat fromFormat) {
        Map<MetadataFormat, SingleSourceTransformer> result = map.get(fromFormat);
        if (result == null) {
            result = new HashMap<MetadataFormat, SingleSourceTransformer>();
            map.put(fromFormat, result);
        }
        return result;
    }

    private void checkResultFormatNotPresent(Map<MetadataFormat, SingleSourceTransformer> resultFormatBuilderMap, MetadataFormat from, MetadataFormat to) throws Exception {
        if (resultFormatBuilderMap.containsKey(to)) {
            throw new Exception("Multiple builders for transformation from " + from.toString() + " to " + to.toString());
        }
    }

    private SingleSourceTransformer createBuilder(MetadataFormat from, MetadataFormat to, File xslt) throws Exception {
        switch (from) {
            case marc21:
                return getMarcBasedBuilder(to, xslt);
            case tei_p5:
                return getTeiBasedBuilder(to, xslt);
            default:
                throw new TransformationException("No builder from " + from.toString() + " to " + to.toString());
        }
    }

    private SingleSourceTransformer getMarcBasedBuilder(MetadataFormat to, File xslt) throws TransformationException {
        switch (to) {
            case ese:
                return builderCache.getMarcBasedXsltEseBuilder(xslt);
            case oai_dc:
                return builderCache.getMarcBasedOaiDcBuilder(xslt);
            default:
                throw new TransformationException("No builder from marc21 to " + to.toString());
        }
    }

    private SingleSourceTransformer getTeiBasedBuilder(MetadataFormat to, File xslt) throws TransformationException {
        switch (to) {
            case ese:
                return builderCache.getTeiBasedXsltEseBuilder(xslt);
            default:
                throw new TransformationException("No builder from tei to " + to.toString());
        }
    }

    public Set<MetadataFormat> derivedFormats(MetadataFormat primaryFormat) {
        Map<MetadataFormat, SingleSourceTransformer> formatTransformerMap = map.get(primaryFormat);
        if (formatTransformerMap == null) {
            return Collections.<MetadataFormat>emptySet();
        } else {
            return formatTransformerMap.keySet();
        }
    }

    public Document buildSecondaryRecordMetadata(MetadataFormat sourceFormat, MetadataFormat destFormat, Document primaryRecord) throws TransformationException {
        SingleSourceTransformer builder = getBuilder(sourceFormat, destFormat);
        if (builder == null) {
            throw new TransformationException("No builder from " + sourceFormat.toString() + " to " + destFormat.toString());
        } else {
            return builder.run(primaryRecord);
        }
    }

    private SingleSourceTransformer getBuilder(MetadataFormat from, MetadataFormat to) {
        Map<MetadataFormat, SingleSourceTransformer> resultFormatBuilderMap = getResultFormatBuilder(from);
        return resultFormatBuilderMap.get(to);
    }
}
